package pl.edu.pw.stud.ltrzaska.logic;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemException;
import org.apache.log4j.Logger;

import pl.edu.pw.stud.ltrzaska.exception.FilesysPolicyException;
import pl.edu.pw.stud.ltrzaska.policy.entity.Filesystem;
import pl.edu.pw.stud.ltrzaska.policy.entity.FilesystemObject;
import pl.edu.pw.stud.ltrzaska.policy.entity.Policy;
import pl.edu.pw.stud.ltrzaska.policy.entity.iface.IFilesystemParent;

/**
 * Przechodzi drzewo systemu plikow i znajduje liste odpowiednikow.
 */
public class FsEquivalentsFinder {

	private static final Logger LOGGER = Logger.getLogger(FsEquivalentsFinder.class.getName());
	private Policy policy;
	private FileObject virtualFsEntry;
	/**
	 * Lista odpowiednikow obiekt polityki poprawnosci - obiekt wirtualnego
	 * systemu plikow. Jezeli w podsystemie nie znaleziono odpowiednika,
	 * odpowiednikiem jest null.
	 */
	private Map<String, LinkedList<FsObjectEquivalent>> equivalents = new HashMap<String, LinkedList<FsObjectEquivalent>>();

	public Map<String, LinkedList<FsObjectEquivalent>> getEquivalents() {
		return equivalents;
	}

	public FsEquivalentsFinder(Policy policy, FileObject filesysEntry) {
		this.policy = policy;
		this.virtualFsEntry = filesysEntry;
	}

	/**
	 * Inicjuje wolanie rekurencyjnej metody <code>visitNode()</code> na
	 * wszystkich wezlach polozonych bezposrednio w wezle glownym polityki.
	 * 
	 * @return
	 * @throws FileSystemException
	 * @throws FilesysPolicyException
	 */
	public void findEquivalents() throws FileSystemException, FilesysPolicyException {
		Filesystem policyFsObj = policy.getFilesystem();

		// zakladamy ze system plikow jest z definicji oczekiwanym
		// odpowiednikiem obiektu filesystem w polityce
		addEquivalent(policyFsObj.getRefname(), new FsObjectEquivalent(policyFsObj, this.virtualFsEntry));
		traverseChildren(policyFsObj, this.virtualFsEntry);
	}

	/**
	 * Wykonuje <code>visitNode()</code> dla kazdego wezla potomnego dla zadanego
	 * <code>FilesystemObject</code> w zadanym nadrzednym wirtualnym systemie
	 * plikow <code>FileObject</code>.
	 * 
	 * @param policyObj
	 * @param conformingFilesysObject
	 * @throws FileSystemException
	 * @throws FilesysPolicyException
	 */
	private void traverseChildren(FilesystemObject policyObj, FileObject conformingFilesysObject) throws FileSystemException, FilesysPolicyException {
		
		if (policyObj != null && policyObj.hasChildren()) {
			IFilesystemParent policyFsParent = (IFilesystemParent) policyObj;
			if (null != policyFsParent.getChildren()) {
				for (FilesystemObject policyObjChild : policyFsParent.getChildren()) {
					this.visitNode(policyObjChild, conformingFilesysObject);
				}
			}
		}
	}

	
	/**
	 * Odwiedza wezel obiektu polityki, uzupelnia informacje o nim oraz szuka
	 * odpowiednikow w wirtualnym systemie plikow.
	 * 
	 * @param policyFsObj
	 * @param parentVirtualFsObj
	 * @param parentPolicyFsObj
	 * @return
	 * @throws FileSystemException
	 * @throws FilesysPolicyException
	 */
	private boolean visitNode(FilesystemObject policyFsObj, FileObject parentVirtualFsObj) throws FileSystemException,
			FilesysPolicyException {

		boolean result = false;

		LOGGER.debug(new StringBuffer("Szukam obiektu polityki: ").append(policyFsObj.getRefname()).append(" w fizycznym obiekcie: ").append(parentVirtualFsObj.getName()).toString());

		List<FileObject> foundVirtualFs = policyFsObj.findVirtualFsEquivalents(parentVirtualFsObj);
		
		if (foundVirtualFs != null && foundVirtualFs.size() > 0) {
			for (FileObject virtualFsEquiv : foundVirtualFs) {
				addEquivalent(policyFsObj.getRefname(), new FsObjectEquivalent(policyFsObj, virtualFsEquiv));
				traverseChildren(policyFsObj, virtualFsEquiv);
			}
			LOGGER.debug("Dopasowano do obiektu polityki [" + policyFsObj.getRefname() + "] " + foundVirtualFs.size() + " odpowiednik(ow).");
		} else {
			LOGGER.debug("Nie dopasowano do obiektu polityki [" + policyFsObj + "] zadnego obiektu systemu plikow w " + parentVirtualFsObj.getName());
			addEquivalent(policyFsObj.getRefname(), new FsObjectEquivalent(policyFsObj, null));
		}
		return result;
	}

	/**
	 * Dodaje ekwiwalent do mapy list ekwiwalentow.
	 * 
	 * @param refname
	 * @param fsObjectEquivalent
	 */
	private void addEquivalent(String refname, FsObjectEquivalent fsObjectEquivalent) {
		if (!equivalents.containsKey(refname)) {
			equivalents.put(refname, new LinkedList<FsObjectEquivalent>());
		}
		equivalents.get(refname).add(fsObjectEquivalent);
	}
}
